package com.tom.demo.common.config;

import com.tom.demo.common.enumeration.RedisTopicEnum;
import com.tom.demo.listener.redis.queue.RedisSubListenerQueueOne;
import com.tom.demo.listener.redis.queue.RedisSubListenerQueueThree;
import com.tom.demo.listener.redis.queue.RedisSubListenerQueueTwo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * redis订阅监听配置
 * Redis发布订阅是一种实时通信机制，消息一旦发布就只投递给当时在线的订阅者
 * 消息不会被存储在任何队列或其他数据结构中等待消费
 * 如果订阅者离线或未能及时处理，这些消息将永久丢失
 * 重新上线的订阅者只能接收到上线后新发布的消息
 */
@Configuration
public class RedisSubConfig {

    @Resource
    private RedisSubListenerQueueOne redisSubListenerQueueOne;
    @Resource
    private RedisSubListenerQueueTwo redisSubListenerQueueTwo;
    @Resource
    private RedisSubListenerQueueThree redisSubListenerQueueThree;
    /**
     * Redis事件监听容器：它是 Spring 应用中接收 Redis 消息的中央组件，负责管理消息监听器并将消息路由到相应的处理程序。
     * 连接管理：通过设置 RedisConnectionFactory，它管理与 Redis 服务器的连接。
     * 事件订阅：专门用于订阅 Redis 的键空间通知（keyspace notifications），比如键过期事件、键修改事件等。
     * 线程处理：它内部维护线程池来异步处理接收到的 Redis 事件，避免阻塞主线程。
     * 在 Redis 键过期监听的场景中，这个容器是必须的，因为 RedisKeyExpireListener 需要通过它来订阅和接收 Redis 服务器发出的键过期通知。简单说，没有这个容器，你的键过期监听器就无法工作。
     * 对于分布式事务中，这个组件可以帮助实现最终一致性模式，在某个操作完成或超时后触发后续处理或补偿操作。
     * @param connectionFactory
     * @return
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {
        //创建redis监听器容器
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        //设置redis连接工厂
        redisMessageListenerContainer.setConnectionFactory(connectionFactory);


        //DDT      设置我们的不同订阅者监听的topic
        // PatternTopic 适用于需要监听多个相似 topic 的场景。
        //ChannelTopic 适用于需要精确监听某个具体 topic 的场景。
        //设置redisSubListenerQueueOne监听器监听的topic (PatternTopic 使用正则表达式模式匹配 topic 名称,可以监听多个相似的 topic。)
        redisMessageListenerContainer.addMessageListener(redisSubListenerQueueOne, new PatternTopic(RedisTopicEnum.SUB_KEY_TOPIC_ONE.getTopic()));
        //设置redisSubListenerQueueTwo监听器监听的topic (ChannelTopic 直接监听指定的 topic 名称,每次只能监听单个 topic)
        redisMessageListenerContainer.addMessageListener(redisSubListenerQueueTwo, new ChannelTopic(RedisTopicEnum.SUB_KEY_TOPIC_TWO.getTopic()));

        //将我们自定义的messageSubListenerThree监听器放入redis监听容器中,并且一次性监听多个topic
        List<ChannelTopic> channelTopicList = new ArrayList<>();
        channelTopicList.add(new ChannelTopic(RedisTopicEnum.SUB_KEY_TOPIC_ONE.getTopic()));
        channelTopicList.add(new ChannelTopic(RedisTopicEnum.SUB_KEY_TOPIC_TWO.getTopic()));
        channelTopicList.add(new ChannelTopic(RedisTopicEnum.SUB_KEY_TOPIC_THREE.getTopic()));
        //redisSubListenerQueueThree这个监听者，同时接收来自于三个topic主题推送过来的消息
        redisMessageListenerContainer.addMessageListener(redisSubListenerQueueThree,channelTopicList);
        return redisMessageListenerContainer;
    }
}
